package com.reggie.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reggie.acl.dataobject.Category;
import com.reggie.acl.dataobject.Dish;
import com.reggie.acl.dataobject.DishFlavor;
import com.reggie.acl.mapper.DishMapper;
import com.reggie.common.enums.ResponseCode;
import com.reggie.common.exception.ReggieException;
import com.reggie.common.util.AssertUtils;
import com.reggie.service.model.bo.DishBO;
import com.reggie.service.model.request.DishPageRequest;
import com.reggie.service.service.CategoryService;
import com.reggie.service.service.DishFlavorService;
import com.reggie.service.service.DishService;
import com.reggie.service.service.SetmealDishService;
import com.reggie.service.service.ShoppingCartService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 菜品管理 服务实现类
 * </p>
 *
 * @author yth
 * @since 2023-02-04
 */
@Slf4j
@Service
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SetmealDishService setmealDishService;

    @Autowired
    private ShoppingCartService shoppingCartService;

    /**
     * 给dto添加categoryName属性
     *
     * @param dishBO dto
     */
    private void addCategoryName(final DishBO dishBO) {
        Long categoryId = dishBO.getCategoryId();
        categoryService.getById(categoryId)
                .ifPresent(e -> dishBO.setCategoryName(e.getName()));
    }

    private void addCategoryName(final List<DishBO> dishBOList) {
        Set<Long> set = dishBOList.stream()
                .map(DishBO::getCategoryId)
                .collect(Collectors.toSet());
        List<Category> categories = categoryService.getByIds(set);
        Map<Long, String> map = categories.stream()
                .collect(Collectors.toMap(Category::getId, Category::getName));
        dishBOList.forEach(e -> e.setCategoryName(map.get(e.getCategoryId())));
    }

    /**
     * 给dto添加flavors属性
     *
     * @param dishBO dto
     */
    private void addFlavors(final DishBO dishBO) {
        Long dishId = dishBO.getId();
        List<DishFlavor> flavors = dishFlavorService.getByDishId(dishId);
        dishBO.setFlavors(flavors);
    }

    private void addFlavors(final List<DishBO> dishBOList) {
        Set<Long> set = dishBOList.stream()
                .map(DishBO::getId)
                .collect(Collectors.toSet());
        List<DishFlavor> flavors = dishFlavorService.getByDishIds(set);
        Map<Long, List<DishFlavor>> map = flavors.stream()
                .collect(Collectors.groupingBy(DishFlavor::getDishId));
        dishBOList.forEach(e -> {
                    List<DishFlavor> flavorList = map.getOrDefault(e.getId(), new ArrayList<>(8));
                    e.setFlavors(flavorList);
                }
        );
    }


    @Override
    public int countByCategoryId(long categoryId) {
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dish::getCategoryId, categoryId);
        return dishMapper.selectCount(wrapper);
    }

    @Override
    @Transactional
    public IPage<DishBO> page(DishPageRequest request) {
        AssertUtils.nonNull(request, ResponseCode.CLIENT_ERROR);
        AssertUtils.nonNull(request.getPage(), ResponseCode.CLIENT_ERROR);
        AssertUtils.nonNull(request.getPageSize(), ResponseCode.CLIENT_ERROR);

        final String name = request.getName();
        final Integer page = request.getPage();
        final Integer pageSize = request.getPageSize();

        LambdaQueryWrapper<Dish> wrapper = Wrappers.lambdaQuery(Dish.class)
                .like(StringUtils.isNotBlank(name), Dish::getName, name)
                .orderByDesc(Dish::getUpdateTime);
        IPage<Dish> dishPage = dishMapper.selectPage(new Page<>(page, pageSize), wrapper);
        IPage<DishBO> dishDtoPage = dishPage.convert(DishBO::new);
        if (CollectionUtils.isNotEmpty(dishDtoPage.getRecords())) {
            addCategoryName(dishDtoPage.getRecords());
        }
        return dishDtoPage;
    }

    @Override
    public void updateStatusBatchIds(int status, List<Long> ids) {
        /*
         考虑到可能需要停售某菜品单个售卖，而不停售该菜品和其它菜品组合的套餐捆绑售卖。
         所以此处不用考虑关联的套餐。
         */
        LambdaUpdateWrapper<Dish> wrapper = Wrappers.lambdaUpdate(Dish.class);
        wrapper.in(Dish::getId, ids)
                .set(Dish::getStatus, status);
        if (dishMapper.update(new Dish(), wrapper) <= 0) {
            log.error("菜品状态更新失败,status={},idList={}", status, ids);
            throw new ReggieException(ResponseCode.SERVER_ERROR, "数据库服务菜品状态更新失败");
        }
    }

    @Override
    public void removeBatchIds(List<Long> ids) {
        // 查询其中“在售”的菜品数量
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Dish::getId, ids).eq(Dish::getStatus, 1);
        int count = dishMapper.selectCount(wrapper);
        if (count > 0) {
            throw new ReggieException(ResponseCode.CLIENT_ERROR, "只能删除“停售”的菜品");
        }
        // 查询关联套餐数量
        count = setmealDishService.countDishByIds(ids);
        if (count > 0) {
            throw new ReggieException(ResponseCode.CLIENT_ERROR, "该菜品与" + count + "个套餐关联，不能删除");
        }
        // 删除口味
        dishFlavorService.removeBatchDishIds(ids);
        // 删除菜品
        dishMapper.deleteBatchIds(ids);
    }

    @Override
    public void saveWithFlavor(DishBO dishBO) {
        // 保存菜品
        if (dishMapper.insert(dishBO) <= 0) {
            log.error("数据库服务菜品保存失败,{}", dishBO);
            throw new ReggieException(ResponseCode.SERVER_ERROR, "数据库服务菜品保存失败");
        }
        // 给菜品口味添加菜品id
        List<DishFlavor> flavors = dishBO.getFlavors();
        Long dishId = dishBO.getId();
        flavors.forEach(flavor -> flavor.setDishId(dishId));
        // 批量保存口味
        dishFlavorService.save(flavors);
    }

    @Override
    public Optional<Dish> getById(long dishId, Integer status) {
        LambdaQueryWrapper<Dish> wrapper = Wrappers.lambdaQuery(Dish.class)
                .eq(Dish::getId, dishId)
                .eq(status != null, Dish::getStatus, status);
        return Optional.ofNullable(dishMapper.selectOne(wrapper));
    }

    @Override
    @Transactional
    public DishBO getWithFlavorById(long id) {
        Dish dish = dishMapper.selectById(id);

        DishBO dishBO = Optional.ofNullable(dish)
                .map(DishBO::new)
                .orElse(null);
        Optional.ofNullable(dishBO)
                .ifPresent(this::addFlavors);
        return dishBO;
    }

    @Override
    @Transactional
    public void update(DishBO dishBO) {
        Dish dish = dishMapper.selectById(dishBO.getId());
        // 原菜品不存在
        if (dish == null) {
            throw new ReggieException(ResponseCode.CLIENT_ERROR, "菜品更新失败，原菜品不存在");
        }
        if (!dish.getName().equals(dishBO.getName())
                || !dish.getPrice().equals(dishBO.getPrice())) {
            // 如果菜品名称或价格更新，更新菜品与套餐关联表中的冗余字段数据
            setmealDishService.updateDish(dishBO);
        }

        if (!dish.getName().equals(dishBO.getName())
                || !dish.getImage().equals(dishBO.getImage())
                || !dish.getPrice().equals(dishBO.getPrice())) {
            // 如果菜品名称或价格或图片更新，更新购物车表中的冗余字段数据
            shoppingCartService.updateDish(dishBO);
        }

        /* 没必要更新订单表冗余数据，否则可能出现以往套餐菜品信息的丢失 */

        // 更新菜品
        if (dishMapper.updateById(dishBO) <= 0) {
            log.error("数据库服务菜品更新失败,{}", dishBO);
            throw new ReggieException(ResponseCode.SERVER_ERROR, "数据库服务菜品更新失败");
        }
        // 删除菜品原本的口味
        List<Long> collect = Stream.of(dishBO.getId()).collect(Collectors.toList());
        dishFlavorService.removeBatchDishIds(collect);
        // 给菜品口味添加菜品id
        List<DishFlavor> flavors = dishBO.getFlavors();
        Long dishId = dishBO.getId();
        for (DishFlavor flavor : flavors) {
            flavor.setDishId(dishId);
            flavor.setId(null);
        }
        // 批量保存口味
        dishFlavorService.save(flavors);
    }

    @Override
    @Transactional
    public List<DishBO> listWithFlavor(Dish dish) {
        Long categoryId = dish.getCategoryId();
        String name = dish.getName();
        Integer status = dish.getStatus();
        LambdaQueryWrapper<Dish> wrapper = Wrappers.lambdaQuery(Dish.class)
                .eq(null != categoryId, Dish::getCategoryId, categoryId)
                .like(StringUtils.isNotBlank(name), Dish::getName, name)
                .eq(status != null, Dish::getStatus, status)
                .orderByAsc(Dish::getSort)
                .orderByDesc(Dish::getUpdateTime);
        List<Dish> dishList = dishMapper.selectList(wrapper);
        // 添加菜品口味数据
        List<DishBO> dishBOList = dishList.stream()
                .map(DishBO::new)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(dishBOList)) {
            this.addFlavors(dishBOList);
        }
        return dishBOList;
    }


}
